/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */
package com.pehulja.mmdo.coursework.math;

import com.google.common.primitives.Doubles;
import com.pehulja.mmdo.coursework.math.exceptions.InvalidNumberArgmentsException;
import java.io.Serializable;

import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Set;

/**
 *
 * @author pehulja
 */
public class PolinomialFunc implements IFunction, Serializable, Cloneable{

    private List <Term> components;

    public PolinomialFunc(double[][]data) throws InvalidNumberArgmentsException {
        if(data.length==0 || data[data.length-1].length!=3)
            throw new InvalidNumberArgmentsException();
        components = new ArrayList<Term>();
        for(int i = 0; i < data.length; i++)
            components.add(new Term(data[i][1], data[i][2], (byte) data[i][0]));
    }
    // Harmonization of such terms in the polynomial and sorting by variables ids
    // TODO: only for linear functions with degree = 1
    @Override
    public boolean simplify() {
        Term current, working;
        for(int i = 0; i < components.size(); i++){
            current = components.get(i);
            for(int j = i+1; j < components.size(); j++){
                working = components.get(j);
                if((current.getId().equals(working.getId())) && current.getDegree().equals(working.getDegree())){
                    current.setFactor(current.getFactor()+working.getFactor());
                    components.remove(j);
                    j--;
                    continue;
                }
            }
        }
        sortByVarIds();
        return true;
    }

    //sorting by variables ids
    @Override
    public void sortByVarIds() {
        int minIndex;
        int n = components.size();
        Term term;
        for(int i = 0; i < n; i++){
            minIndex = i;
            for(int j = i + 1; j < n; j++){
                if(components.get(j).getId()<components.get(minIndex).getId())
                    minIndex = j;
            }
            term = (Term) components.get(minIndex).copy();
            components.set(minIndex, (Term) components.get(i).copy());
            components.set(i, term);
        }
    }

    @Override
    public int getVariablesCount() {
        Set <Byte> varSet = new HashSet<Byte>();
        for(Term i:components){
            if(i.getId()!=null)
                varSet.add(i.getId());
        }
        return varSet.size();
    }


    @Override
    public boolean isMonoVariable() {
        Set <Byte> varSet = new HashSet<Byte>();
        for(Term i:components){
            if(i.getId()!=null)
                if(varSet.add(i.getId())==false)
                    return false;
        }
        return true;
    }

    // Adding 0 variables components to complete list
    public void completeToAllVars(List<Byte> vars){
        vars.removeAll(getListVarsId());
        System.out.println(vars);
        for(Byte i:vars)
            getComponents().add(new Term(0, 0, i));
        sortByVarIds();
    }

    public PolinomialFunc() {
        components = new ArrayList<Term>();
    }
    public PolinomialFunc(int n) {
        components = new ArrayList<Term>(n);
    }

    @Override    
    public List<Term> getComponents() {
        return components;
    }

    public void setComponents(List<Term> components) {
        this.components = components;
    }
    
    @Override
    public Double countValue (double [] inputVector){
        Double result = 0d;
        /*if(components.size() != inputVector.length)
            return null;*/
        for (int i = 0; i < Math.min(inputVector.length, components.size()); i++){
            result += components.get(i).count(inputVector[i]);
        }
        return result;
    }

    @Override
    public String toString() {
        StringBuilder builder = new StringBuilder();
        for(Term i:components){
            builder.append(i.toString());
        }
        return builder.toString();
    }

    @Override
    public List<Double> getListFactors() {
        List <Double> list = new ArrayList<Double>();
        for(Term i:components)
            list.add(i.getFactor());
        return list;
    }

    @Override
    public double[] getListFactorsArray() {
        double[] list = new double[components.size()];
        for(int i = 0; i < components.size(); i++)
            list[i]=components.get(i).getFactor();
        return list;
    }
    @Override
    public List<Double> getListFactorsArray(List<Integer> componentColumnRestriction) {
        List <Double> list = new ArrayList<Double>();
        for(int i = 0; i < components.size(); i++)
            if(componentColumnRestriction==null || componentColumnRestriction.contains(i))
                list.add(components.get(i).getFactor());
        return list;
    }

    @Override
    public List<Byte> getListVarsId() {
        List <Byte> list = new ArrayList<Byte>();
        for(Term i:components)
            list.add(i.getId());
        return list;
    }

    @Override
    public double[] getListVarsIdArray() {
        double[] list = new double[components.size()];
        for(int i = 0; i < components.size(); i++)
            list[i]=components.get(i).getId();
        return list;
    }

    @Override
    public boolean setListFactors(List<Double> list) {
        if(list.size()!=components.size())
            return false;
        for (int i=0; i < list.size(); i++){
            components.get(i).setFactor(list.get(i));
        }
        return true;
    }

    public void Multiply(double factor){
        for(Term term:this.getComponents())
            term.setFactor(term.getFactor() * factor);
    }

    public IFunction copy() {
        PolinomialFunc func = new PolinomialFunc();
        List <Term> list = new ArrayList<Term>();
        for(Term term:components)
            list.add(term.copy());
        func.setComponents(list);
        return func;
    }
    
    public boolean checkIfListFactorsToZero(Equation.InequalitySight sight){
        boolean result = true;
        for(Term term:components){
            if(sight==Equation.InequalitySight.BIGGER)
                result = term.getFactor()>0;
            else if(sight==Equation.InequalitySight.BIGGERorEQ)
                result = term.getFactor()>=0;
            else if(sight==Equation.InequalitySight.EQ)
                result = term.getFactor()==0;
            else if(sight==Equation.InequalitySight.SMALLER)
                result = term.getFactor()<0;
            else if(sight==Equation.InequalitySight.SMALLERorEQ)
                result = term.getFactor()<=0;
            if(!result)
                return result;
        }
        return result;
    }
    
}
